在軟體開發過程中,最不確定的因素是「需求」。因為通常要做的系統是沒人用過的新系統,常常使用者沒看到系統前也說不清楚自己要什麼,可能一開始說要個「發光的燈管加握把」(這什麼?),等你做給他的時候,他說他想要的是「光劍」...這種落差越晚發現,修改成本就越大。為了及早發現這種落差,開發者會用各種方式去逼出真實的需求,包括寫規格書、畫 wireframe、送小禮物(?)等...
製作雛形系統是一種好方法,因為使用者可以實際操作模擬的頁面,較能得到接近真實需求的回饋,但是如果製作雛形系統要用另一套語言或方法,成本太高,未必值得。
zul 中的 <zscript>
讓你可以內嵌 Java 程式碼在 zul ,快速製作可執行的雛形系統,待使用者確認後,繼續以該 zul 頁面為基礎擴增成為上線系統。
可用來塞樣本資料。
<zscript><![CDATA[
String force = "願原力與你同在";
]]></zscript>
可用來製造樣本使用者。
<zscript><![CDATA[
Sessions.getCurrent().setAttribute("user", "天行者路克");
]]></zscript>
直接把 java code 寫在事件屬性值中,就會被執行。
<button onClick='Messagebox.show("資料已送出")' label="送出"/>
注意使用 java 字串時,因為需要用雙引號,因此屬性值外層要用單引號。
可以用來模擬功能。
<zscript><![CDATA[
public void costaRicaBlessing(){
Messagebox.show("pura vida");
}
]]></zscript>
<button onClick='costaRicaBlessing()' label="哥斯大黎加的祝福"/>
元件的 ID 就是參照到元件物件的變數名稱,可拿來呼叫元件 API:
<intbox id="box"/>
<zscript><![CDATA[
box.setValue(18);
]]>
</zscript>
以上這些就足以讓你做出一個簡單的可互動的雛形系統。
不過 zscript 是犧牲效能來達到 runtime 編譯 java 的效果,因此不建議在生產環境使用。為了避免不小心誤用,你可以設定關閉 (The disable-zscript Element),如果有人用了會立即丟出例外,可以及早發現。
用過 JSP 的應該都會知道 EL,是一個簡潔的存取 server-side 物件的語言,zul 也支援幾近相同的語法,也有許多可直接使用的隱含物件。
定義在<zscript>
中的變數可直接以變數名稱存取:
<zscript><![CDATA[
Date now = new Date();
]]></zscript>
<datebox value="${now}"/>
隱含變數跟 id 都可存取元件:
<window title="EL範例">
<textbox id="tb" value="${self.parent.title}"/>
${tb.value}
</window>
self
代表 Textbox 物件,${self.parent.title}
的結果就是其父元件的 title,也就是 EL範例
tb
, Textbox 的 ID,在 EL 可用來存取 Textbox 物件的參照,因此 ${tb.value}
等同呼叫 Textbox.getValue()
,結果也是EL範例
<button label="Enter" if="${not empty param.edit}"/>
true
就創建該元件,反之則不創建。這裡的 scope 指得是 Java EE scopes,你可把任何物件存入這些 scope 的 attribute 中,用來分享給同樣 scope 的其他物件。除了 Java EE 本身定義的幾種 scope 之外,ZK 也另外定義兩種 scope。
component scope: 這應該很容易理解,元件存在時,存入的 attribute 就存在,元件消滅就跟著消滅。
desktop scope: 這是 ZK 創造的範疇,因為每個瀏覽器 tab 的 JavaScript widget 都是獨立,但是卻又同屬一個 session ,因此 ZK 需要一個 scope 來區分同一個 session 下兩個不同 tab 中的元件,因此創造了 desktop,你可以把它視為對應到一個瀏覽器 tab,一個 desktop 存放著整個頁面的元件樹,當 tab 關閉時,desktop 就消滅,或瀏覽器重載頁面 (reload) 也會創造一個新 desktop。因此存在 desktop attribute 中的資料,其下的所有元件都能存取到。
當一個 EL 變數在被解析為 scope attribute 時,ZK 會從小的 scope 依次搜尋到大的 scope 直到找到為止,順序如下:
HTTP request/AJAX request 在 ZK 內部都被封裝成 Execution 物件,因此在 ZK文件中多半以 execution 來指稱 request。
若是完全找不到,也不會顯示任何錯誤,就是不顯示任何文字在頁面上。因為這個特性,若是兩個 attribute 有相同的 key 存在不同的 scope 中,小 scope 的 attribute 會先被找到就不會再往大 scope 找了,要注意這種「遮蔽」現象。
<div id="parent">
<zscript><![CDATA[
//the smaller scope (lower one) can shadow the upper one
application.setAttribute("myname", "in application");
session.setAttribute("myname", "in session");
desktop.setAttribute("myname", "in desktop");
page.setAttribute("myname", "in page");
parent.setAttribute("myname", "in component");
execution.setAttribute("myname", "in execution");
]]></zscript>
解析結果:
<label style="font-weight: bold" value="${myname}"/>
</div>
${'Hi, ' += firstname += ' ' += lastname}
ZK 根據 EL 表達式從目標物件取出值的過程我們稱「估值 (evaluation)」,ZK 只在創建元件的時後會做 EL 估值,之後如果元件只是透過 ajax 更新資料或外觀,ZK 不會再重估值一次。何時是元件創建時呢?
更多細節請參考 ZUML_Reference/EL_Expressions。
在做雛形頁面時,可能部分頁面需求沒有對應的元件可用,需要自行以 HTML 設計,因此總是有需要混用 HTML tag 的時候,這時你需要要先宣告 namespace並指定前綴,並在每個 HTML tag 前方加上指定前綴:
<zk xmlns:h="native">
<h:h1>混用 HTML</h:h1>
以上例來說,ZK 就知道 <h:h1>
是 HTML tag 而不是 ZK 元件。
如果你要貼上大量 HTML tag,例如可能是設計師設計好的 HTML 頁面,你可以在某一個 tag 宣告 HTML 是預設的 namespace,這樣ZK 就會認定其下方的都是 HTML tag,而不需另外加前綴:
<h:div xmlns="native">
<h2>預設都是 HTML tag</h2>
<span>不需要加前綴</span>
</h:div>
更多細節請參考 ZUML Reference/ZUML/Namespaces